use common::error::ErrorContextTrait;
use common::file_system::{read_general_json, write_to_file};
use serde_json::Value;
use std::path::Path;

pub fn load_package_json(project_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
    let dev_package = read_general_json(Path::new("./package.json"))
        .with_error_context("当前目录下存在package.json")?;
    let project_package_json_path = project_dir.join("package.json");
    let project_package = read_general_json(&project_package_json_path).with_error_context(
        &format!("该目录下不存在package.json:{:?}", project_package_json_path),
    )?;

    let updated_project_package = merge_package(&dev_package, &project_package)?;
    write_to_file(
        &project_package_json_path,
        &updated_project_package.to_string(),
    )?;

    Ok(())
}

fn merge_package(
    dev_package: &Value,
    project_package: &Value,
) -> Result<Value, Box<dyn std::error::Error>> {
    // 将 project_package 转为可变克隆，以便进行修改
    let mut project_package = project_package.clone();

    // 替换 entryInfo
    if let Some(dev_entry_info) = dev_package.get("entryInfo").cloned() {
        project_package["entryInfo"] = dev_entry_info;
    }

    // 合并 peerDependencies 到 dependencies
    if let Some(dev_peer_dependencies) = dev_package
        .get("peerDependencies")
        .and_then(|v| v.as_object())
    {
        if let Some(project_dependencies) = project_package
            .get_mut("dependencies")
            .and_then(|v| v.as_object_mut())
        {
            for (key, value) in dev_peer_dependencies {
                if !project_dependencies.contains_key(key) {
                    project_dependencies.insert(key.clone(), value.clone());
                }
            }
        } else {
            project_package["dependencies"] = Value::Object(dev_peer_dependencies.clone());
        }
    }

    Ok(project_package)
}

#[cfg(test)]
mod test {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_merge_pacage() {
        let dev_package = json!({
        "name": "todolist_base",
        "version": "0.0.1",
        "description": "todolist list base",
        "type": "module",
        "main": "src/index",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
        },
        "entryInfo": {
          "name": "Base"
        },
        "peerDependencies": {
          "vue": "^3.5.12",
          "lodash": "^3.0.5"
        },
        "repository": {
          "type": "git",
          "url": "git@gitee.com:hanshu_alan/todolist_base.git"
        },
        "keywords": [
          "base"
        ],
        "author": "zongying_cao@163.com",
        "license": "MIT"
              });

        let project_package = json!({
          "name": "project",
          "private": true,
          "version": "0.0.1",
          "type": "module",
          "scripts": {
            "dev": "vite",
            "build": "vue-tsc -b && vite build",
            "preview": "vite preview"
          },
          "dependencies": {
            "vue": "^3.5.13"
          },
          "devDependencies": {
            "@vitejs/plugin-vue": "^5.2.1",
            "@vue/tsconfig": "^0.7.0",
            "typescript": "~5.6.2",
            "vite": "^6.0.5",
            "vue-tsc": "^2.2.0"
          }
        });

        let expected = json!({
          "name": "project",
          "private": true,
          "version": "0.0.1",
          "type": "module",
          "scripts": {
            "dev": "vite",
            "build": "vue-tsc -b && vite build",
            "preview": "vite preview"
          },
        "entryInfo": {
            "name": "Base"
        },
          "dependencies": {
            "vue": "^3.5.13",
            "lodash": "^3.0.5"
          },
          "devDependencies": {
            "@vitejs/plugin-vue": "^5.2.1",
            "@vue/tsconfig": "^0.7.0",
            "typescript": "~5.6.2",
            "vite": "^6.0.5",
            "vue-tsc": "^2.2.0"
          }
        });

        let result = merge_package(&dev_package, &project_package).expect("merge error");

        assert_eq!(result, expected);
    }
}
